/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

/*!
  \file nodeList.cxx
  \author Cory Dixon
*/

#include <stdlib.h>
#include <stdio.h>

#include "clientList.h"
#include "fpManager.h"
#include "nodeList.h"
#include "vcgui3.h"
#include "vcConfig.h"
#include "threadedPipe.h"
#include "dataDistribution.h"

/*<---Extern Variables---->*/
extern GuiStruct    guiStr;

/* columns for GtkTree */
enum
{
	NAME_COLUMN = 0,
	SUBSCRIBE_COLUMN,
	CMD_COLUMN,
	COLOR_COLUMN,
	COLORSET_COLUMN,
	NUM_COLUMNS
};

NodeList::NodeList()
{
	node_list  = NULL;
	tree_model = NULL;
	treeview   = NULL;

	for(int i=0; i<MAX_CLIENTS; i++)
		node_timedout[i] = false;
}

//-- clean up
NodeList::~NodeList()
{
	// tree_model
	if( tree_model )
		g_object_unref (tree_model);
	if( treeview )
		gtk_widget_destroy (GTK_WIDGET(treeview));
}


void NodeList::init()
{
	//cout << "NodeList::init()" << endl;
	if( !guiStr.vcgui ) {
		cout << "NodeList::init() -- guiStr.vcgui not defined" << endl;
		return;
	}
	
#ifdef USE_GDK_THREAD
	// GTK/GDK thread enter
	gdk_threads_enter();
#endif

	node_list = (GtkWidget *)gtk_builder_get_object(guiStr.vcgui->getBuilder(), "node_list");

	// create tree store model
	tree_model = gtk_tree_store_new (NUM_COLUMNS,
			G_TYPE_STRING,  // name
			G_TYPE_BOOLEAN, // subscribed
			G_TYPE_INT,     // cmd id
			G_TYPE_STRING,  // color string
			G_TYPE_BOOLEAN);// use color

	// create tree view 
	treeview = GTK_TREE_VIEW(gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_model)));

	// setup tree functionality
	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), FALSE);
	gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), GTK_TREE_VIEW_GRID_LINES_NONE);
	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW (treeview), FALSE);
	gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (treeview), FALSE);
	gtk_tree_view_set_reorderable(GTK_TREE_VIEW (treeview), FALSE);
	gtk_tree_view_set_enable_search(GTK_TREE_VIEW (treeview), FALSE);

	GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
	gtk_tree_selection_set_mode( selection, GTK_SELECTION_NONE);

	// draw the view of the tree
	format_treeview();
	setColor( GTK_WIDGET(treeview) );

	// add to container
	gtk_widget_show( GTK_WIDGET(treeview));
	gtk_container_add (GTK_CONTAINER (node_list), GTK_WIDGET(treeview));

	// finally, setup timer to refresh the list
	g_timeout_add (250, this->timeout_cb, this);
	
#ifdef USE_GDK_THREAD
	// GTK/GDK thread leave
	gdk_flush();
	gdk_threads_leave();
#endif
}

void NodeList::format_treeview()
{
	gint col_offset;
	GtkCellRenderer *renderer;

	// column for system parameter names 
	renderer = gtk_cell_renderer_text_new ();
	g_object_set (renderer, "xalign", 0.0, NULL);
	col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
			-1, "Node", renderer, 
			"text", NAME_COLUMN,
			"cell-background",COLOR_COLUMN,
			"cell-background-set",COLORSET_COLUMN,
			NULL);

	// plot column 
	renderer = gtk_cell_renderer_toggle_new ();
	g_object_set (renderer, "xalign", 1.0, NULL);
	g_object_set_data (G_OBJECT (renderer), "column", (gint *)SUBSCRIBE_COLUMN);
	col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
			-1, "Subscribe", renderer, 
			"active", SUBSCRIBE_COLUMN,
			"cell-background",COLOR_COLUMN,
			"cell-background-set",COLORSET_COLUMN,
			NULL);

	g_signal_connect (renderer, "toggled", G_CALLBACK (NodeList::item_toggled), tree_model);
}

// We assume that the shared memory area has been locked for us, since
// this function is called by vcgui when it receives a CMD
void NodeList::addNodes() 
{
	GtkTreeIter iter;
	Client * a_client;

	//cout << "NodeList::addNodes()" << endl;

	int clientInd = 0;
	while( (a_client = guiStr.shm_ptr->clients.getClient(clientInd)) != NULL) {
		if(a_client->state.init == 0) {
			//---------- New Client ----------//
			//cout << "NodeList::addNode() -- " << a_client->name << endl;
			guiStr.vcgui->node_windows[clientInd].create_node_window(a_client);

			// create system level tag
			gtk_tree_store_append (tree_model, &iter, NULL);
			gtk_tree_store_set (tree_model, &iter,
					NAME_COLUMN, a_client->name.c_str(),
					SUBSCRIBE_COLUMN, FALSE,
					CMD_COLUMN, 0,
					COLOR_COLUMN, NULL,
					COLORSET_COLUMN, FALSE,
					-1);


			// Add command capabilities 
			GtkTreeIter child_iter;

			//---------- CMD_TELEM ----------//
			if(a_client->capabilities.find(CMD_TELEM)) {
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "AP Telemetry",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_TELEM,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);
			}
			//---------- CMD_GPS_TELEM ----------//
			if(a_client->capabilities.find(CMD_GPS_TELEM)) {
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "GPS Telemetry",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_GPS_TELEM,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);
			}
			//---------- CMD_SONDE ----------//
			if(a_client->capabilities.find(CMD_SONDE)) {
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "PTH Sensor",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_SONDE,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);
			}
			//---------- CMD_WIND ----------//
			if(a_client->capabilities.find(CMD_WIND)) {
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "Wind Data",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_WIND,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);
			}
			//---------- CMD_FP ----------//
			if(a_client->capabilities.find(CMD_FP_SUBS)) 
			{
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "Flight Plans",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_FP_SUBS,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);				
			}
			//---------- CMD_LINK_STATUS ----------//
			if(a_client->capabilities.find(CMD_LINK_STATUS)) {
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "WiFi Sensor",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_LINK_STATUS,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);				
			}
			//---------- CMD_TARGET_STATUS ----------//
			if(a_client->capabilities.find(CMD_TARGET_STATUS)) {
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "Target Status",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_TARGET_STATUS,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);
			}
			//---------- CMD_TRACK ----------//
			if(a_client->capabilities.find(CMD_TRACK)) 
			{
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "Track Node",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_TRACK,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);				

#if 0
				GtkTreeIter link_iter;
				Client * track_client;
				int j = 0;
				while( (track_client = guiStr.shm_ptr->clients.getClient(j)) != NULL) {
					if(track_client->name != a_client->name) {
						if(track_client->capabilities.find(CMD_TELEM) || track_client->capabilities.find(CMD_GPS_TELEM)) {
							gtk_tree_store_append (tree_model, &link_iter, &child_iter);
							gtk_tree_store_set (tree_model, &link_iter,
									NAME_COLUMN, track_client->name.c_str(),
									SUBSCRIBE_COLUMN, FALSE,
									CMD_COLUMN, CMD_TRACK,
									COLOR_COLUMN, NULL,
									COLORSET_COLUMN, FALSE,
									-1);				
						}
					}
					j++;
				}
#endif
			}
			//---------- CMD_CHAIN ----------//
			if(a_client->capabilities.find(CMD_CHAIN)) 
			{
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "Chain Node",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_CHAIN,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);				
			}

			//---------- CMD_ANT_PAS_TELEM ----------//
			if(a_client->capabilities.find(CMD_ANT_PAS_TELEM)) 
			{
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "Phased Array",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_ANT_PAS_TELEM,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);				
			}
			//---------- CMD_ANT_APS_TELEM ----------//
			if(a_client->capabilities.find(CMD_ANT_APS_TELEM)) 
			{
				gtk_tree_store_append (tree_model, &child_iter, &iter);
				gtk_tree_store_set (tree_model, &child_iter,
						NAME_COLUMN, "Tracking Antenna",
						SUBSCRIBE_COLUMN, FALSE,
						CMD_COLUMN, CMD_ANT_APS_TELEM,
						COLOR_COLUMN, NULL,
						COLORSET_COLUMN, FALSE,
						-1);				
			}
#if 0
			//--------- given my capabilties, make sure others know about them ---------//
			if( a_client->capabilities.find(CMD_TELEM) || a_client->capabilities.find(CMD_GPS_TELEM) ) {

				GtkTreeIter   sys_iter;
				gboolean      valid;
				gchar       * sys_name;
				gchar       * cmd_name;
				gint          cmd_id;
				Client      * track_client;

				valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree_model), &sys_iter);
				while (valid) {
					// get data from current row
					gtk_tree_model_get (GTK_TREE_MODEL (tree_model), &sys_iter, 
							NAME_COLUMN, &sys_name,
							-1);

					track_client = guiStr.shm_ptr->clients.getClient(sys_name);
					if( track_client ) {
						if(track_client->capabilities.find(CMD_TRACK) && track_client->name != a_client->name) {
							if( gtk_tree_model_iter_has_child( GTK_TREE_MODEL (tree_model), &sys_iter ) ) {
								valid = gtk_tree_model_iter_children( GTK_TREE_MODEL (tree_model), &child_iter, &sys_iter);
								while (valid) {
									gtk_tree_model_get (GTK_TREE_MODEL (tree_model), &child_iter, 
											NAME_COLUMN, &cmd_name,
											CMD_COLUMN, &cmd_id,
											-1);

									if( cmd_id == CMD_TRACK ) {
										GtkTreeIter tmp_iter;
										gtk_tree_store_append (tree_model, &tmp_iter, &child_iter);
										gtk_tree_store_set (tree_model, &tmp_iter,
												NAME_COLUMN, a_client->name.c_str(),
												SUBSCRIBE_COLUMN, FALSE,
												CMD_COLUMN, CMD_TRACK,
												COLOR_COLUMN, NULL,
												COLORSET_COLUMN, FALSE,
												-1);

									}

									g_free (cmd_name);
									valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (tree_model), &child_iter);
								}
							} 
						}
					}

					g_free (sys_name);
					valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (tree_model), &sys_iter);
				}

			}
#endif

			//--------- set state as added ----------//
			a_client->state.init = 1;
		}

		/*--------- check next client ----------*/
		clientInd++;
	}

}

void NodeList::refreshNodes() 
{
	Client * a_client;

	//cout << "NodeList::refreshNodes()" << endl;

	GtkTreeIter  sys_iter;
	gboolean     valid;
	gchar       *sys_name;
	gint sys_cmd_id;

	//cout << "NodeList::refreshNodes - mutex_lock" << endl;
	guiStr.vcgui->mutex_lock();

	valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree_model), &sys_iter);
	while (valid) {
		// get data from current row
		gtk_tree_model_get (GTK_TREE_MODEL (tree_model), &sys_iter, 
				NAME_COLUMN, &sys_name,
				CMD_COLUMN, &sys_cmd_id,
				-1);

		a_client = guiStr.shm_ptr->clients.getClient(sys_name);
		if( a_client ) {

			GtkTreeIter child_iter;
			gboolean onOff;
			gchar *cmd_name;
			gint cmd_id;

			if( gtk_tree_model_iter_has_child( GTK_TREE_MODEL (tree_model), &sys_iter ) ) 
			{
				valid = gtk_tree_model_iter_children( GTK_TREE_MODEL (tree_model), &child_iter, &sys_iter);
				while (valid) {
					gtk_tree_model_get (GTK_TREE_MODEL (tree_model), &child_iter, 
							NAME_COLUMN, &cmd_name,
							SUBSCRIBE_COLUMN, &onOff,
							CMD_COLUMN, &cmd_id,
							-1);

					// update this tree item with received linkstatus values
					if( cmd_id == CMD_CHAIN ) {
						//GtkTreeIter link_iter;
						//static bool linkstatusID_init[MAX_CLIENTS];

						// set color if in onboard control mode
						if( a_client->telemetry.navigation.OBtype == OB_CHAIN ) {
							//if( !onOff )
							//gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, SUBSCRIBE_COLUMN, 1, -1);

							if( a_client->telemetry.navigation.OBmode == OB_INIT ) 
								gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, COLORSET_COLUMN, TRUE, COLOR_COLUMN, "yellow", -1);
							else if( a_client->telemetry.navigation.OBmode == OB_ERROR ) 
								gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, COLORSET_COLUMN, TRUE, COLOR_COLUMN, "red", -1);
							else if( a_client->telemetry.navigation.OBmode > OB_INIT ) 
								gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, COLORSET_COLUMN, TRUE, COLOR_COLUMN, "light blue", -1);
						} else
							gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, COLORSET_COLUMN, TRUE, COLOR_COLUMN, NULL, -1);

#if 0
						for(int i=0; i < MAX_CLIENTS; i++ ) {
							if( a_client->linkstatus[i].last_rx > 0 && linkstatusID_init[i] == false) {
								linkstatusID_init[i] = true;
								char idStr[32];
								if( a_client->linkstatus[i].ip[0] == 0 ) {
									sprintf(idStr,"%02X:%02X:%02X:%02X:%02X:%02X",
											a_client->linkstatus[i].mac[0],
											a_client->linkstatus[i].mac[1],
											a_client->linkstatus[i].mac[2],
											a_client->linkstatus[i].mac[3],
											a_client->linkstatus[i].mac[4],
											a_client->linkstatus[i].mac[5]);
								} else {
									sprintf(idStr,"%i.%i.%i.%i",
											a_client->linkstatus[i].ip[0],
											a_client->linkstatus[i].ip[1],
											a_client->linkstatus[i].ip[2],
											a_client->linkstatus[i].ip[3]);

									in_addr addr;
									addr.s_addr = *((in_addr_t *)a_client->linkstatus[i].ip);
									Client * b_client = guiStr.shm_ptr->clients.getClient(addr);
									if( b_client )
										strcpy(idStr, b_client->name.c_str());
								}

								gtk_tree_store_append (tree_model, &link_iter, &child_iter);
								gtk_tree_store_set (tree_model, &link_iter,
										NAME_COLUMN, idStr,
										SUBSCRIBE_COLUMN, FALSE,
										CMD_COLUMN, CMD_CHAIN,
										-1);
							}
						}
#endif
					}
					else if( cmd_id == CMD_TRACK ) 
					{
						// set color if in onboard control mode
						if( a_client->telemetry.navigation.OBtype == OB_TRACK ) {
							//if( !onOff )
							//gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, SUBSCRIBE_COLUMN, 1, -1);

							if( a_client->telemetry.navigation.OBmode == OB_INIT ) 
								gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, COLORSET_COLUMN, TRUE, COLOR_COLUMN, "yellow", -1);
							else if( a_client->telemetry.navigation.OBmode == OB_ERROR ) 
								gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, COLORSET_COLUMN, TRUE, COLOR_COLUMN, "red", -1);
							else if( a_client->telemetry.navigation.OBmode > OB_INIT ) 
								gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, COLORSET_COLUMN, TRUE, COLOR_COLUMN, "light blue", -1);
						} else
							gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, COLORSET_COLUMN, TRUE, COLOR_COLUMN, NULL, -1);
					}

					g_free (cmd_name);
					valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (tree_model), &child_iter);
				}
			} 

			//-------------- update time status ------------------//
			int refNum = guiStr.shm_ptr->clients.getRefNum( a_client );
			if( a_client->state.timed_out == 1 && !node_timedout[refNum] ) {
				node_timedout[refNum] = TRUE;
				gtk_tree_store_set (GTK_TREE_STORE (tree_model), &sys_iter, 
								COLOR_COLUMN, "red", 
								COLORSET_COLUMN, TRUE,
								-1);
			} else if ( !a_client->state.timed_out && node_timedout[refNum] ) {
				node_timedout[ refNum ] = FALSE;

				gtk_tree_store_set (GTK_TREE_STORE (tree_model), &sys_iter, COLOR_COLUMN, NULL, -1);
				if( gtk_tree_model_iter_has_child( GTK_TREE_MODEL (tree_model), &sys_iter ) ) {
					valid = gtk_tree_model_iter_children( GTK_TREE_MODEL (tree_model), &child_iter, &sys_iter);
					while (valid) {
						gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, 
								COLOR_COLUMN, NULL, 
								SUBSCRIBE_COLUMN, FALSE, -1);
						valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (tree_model), &child_iter);
					}
				}
			}

		}

		// clean up
		g_free (sys_name);
		// next tree item
		valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (tree_model), &sys_iter);
	}

	guiStr.vcgui->mutex_unlock();
	//cout << "NodeList::refreshNodes - mutex_unlock" << endl;

}

void NodeList::streamSubscription(String name, uint8_t cmd_id, int onOff)
{
	Client * a_client = guiStr.shm_ptr->clients.getClient(name);
	if(a_client != NULL) {
		//cout << "NodeList -- subscribing " << name << ":" << DefinedCommands[cmd_id].name << ":" << onOff << endl;
		guiStr.vcgui->subscribe(cmd_id, a_client, onOff, ThreadedPipe::INBOARD_TX);
	}
}

void NodeList::item_toggled (GtkCellRendererToggle *cell,
		gchar                 *path_str,
		gpointer               data)
{
	GtkTreeModel *model = (GtkTreeModel *)data;
	GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
	GtkTreeIter iter;
	gboolean onOff;
	gchar *col_name;
	gint cmd_id;

	//cout << "NodeList::item_toggled: path=" << path_str << endl;

	// get toggled iter 
	gtk_tree_model_get_iter (model, &iter, path);

	// get data from current row
	gtk_tree_model_get (model, &iter, 
			NAME_COLUMN, &col_name,
			SUBSCRIBE_COLUMN, &onOff,
			CMD_COLUMN, &cmd_id,
			-1);

	// do something with the value
	onOff ^= 1;

	// set new value 
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter, SUBSCRIBE_COLUMN, onOff, -1);
	if( !onOff )
		gtk_tree_store_set (GTK_TREE_STORE (model), &iter, COLOR_COLUMN, NULL, -1);

	//GtkTreeIter child_iter;
	GtkTreeIter parent_iter;
	GtkTreeIter tmp_iter;
	gboolean valid;

	// get system name
	gchar	*sys_name = NULL;
	parent_iter = iter;
	//gint  child_cmd_id;

	valid = gtk_tree_model_iter_parent(model, &tmp_iter, &iter);
	while (valid) {
		parent_iter = tmp_iter;
		valid = gtk_tree_model_iter_parent(model, &tmp_iter, &parent_iter);
	}
	gtk_tree_model_get (model, &parent_iter, NAME_COLUMN, &sys_name, -1);


	if( gtk_tree_model_iter_has_child( model, &iter ) ) {
		// if it has a child, then it is a system and we have a window, find 
		// the window and toggle its display
		int i = 0;
		while( guiStr.vcgui->node_windows[i].getClient()->name != sys_name && i < MAX_CLIENTS )
			i++;
		if( i >= MAX_CLIENTS ) {
			cout << "NodeList::item_toggled -- could not find " << sys_name << " in the window list" << endl;
		} else {
			if( onOff )
				guiStr.vcgui->node_windows[i].show();
			else
				guiStr.vcgui->node_windows[i].hide();
		}

	} else {
		// finally, check the current box. If its a sub-child do nothing
		// otherwise send a stream subscription
		switch( cmd_id ) {
			case CMD_CHAIN: case CMD_TRACK:
				break;
			case CMD_FP_SUBS: 
					if( !onOff ) {
						FPManager *fpM = (FPManager *)guiStr.config.gpsmap->layer_manager[GPSMap::FP_LAYER];
						if( fpM ) {
							Client *client = guiStr.shm_ptr->clients.getClient(sys_name);
							if( client )
								fpM->clear_client( guiStr.shm_ptr->clients.getRefNum( client ) );
						}
					}
					// let it fall through to set subscription
			default:
				if( cmd_id > 0 && cmd_id < CMD_MAX_NUM )
					NodeList::streamSubscription(sys_name, cmd_id, onOff);
		}
	}

	// clean up 
	g_free (sys_name);
	g_free (col_name);
	gtk_tree_path_free (path);
}

// use this function to indicate a command or ACK has been received
void NodeList::setCMD_RX(String name, uint8_t cmd_id, int ack_nack)
{
	GtkTreeIter  sys_iter, child_iter;
	gboolean     valid;
	gchar       *sys_name;
	gint         child_cmd_id;

	//cout << "NodeList::setCMD_RX - name=" << name << " [" << (int)cmd_id << "]  ackNack=" << ack_nack << endl;
	
	valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree_model), &sys_iter);
	while (valid) {
		// get data from current row
		gtk_tree_model_get (GTK_TREE_MODEL (tree_model), &sys_iter, 
				NAME_COLUMN, &sys_name,
				-1);

		if( name == sys_name ) {
			if( gtk_tree_model_iter_has_child( GTK_TREE_MODEL(tree_model), &sys_iter ) ) {

				valid = gtk_tree_model_iter_children( GTK_TREE_MODEL(tree_model), &child_iter, &sys_iter);
				while (valid) {
					gtk_tree_model_get (GTK_TREE_MODEL(tree_model), &child_iter, 
							CMD_COLUMN, &child_cmd_id,
							-1);

					if( child_cmd_id == cmd_id || (child_cmd_id == CMD_TELEM && cmd_id == CMD_GPS_TELEM) ) {

						switch (cmd_id) {
							case CMD_CHAIN: case CMD_TRACK:
								if( ack_nack == 0 ) {
									gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, 
											COLOR_COLUMN, "red",
											COLORSET_COLUMN, TRUE,
											SUBSCRIBE_COLUMN, 0, 
											-1);
								} else if( ack_nack == 1 ) {
									gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, 
											COLOR_COLUMN, "light grey", 
											COLORSET_COLUMN, TRUE,
											SUBSCRIBE_COLUMN, 1, 
											-1);
								}
								break;

							case CMD_GPS_TELEM:
								if( child_cmd_id == CMD_TELEM ) 
									gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, 
											SUBSCRIBE_COLUMN, 1,  
											COLORSET_COLUMN, TRUE,
											COLOR_COLUMN, "yellow", 
											-1);
								else
									gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, 
											SUBSCRIBE_COLUMN, 1,  
											COLOR_COLUMN, "green", 
											COLORSET_COLUMN, TRUE,
											-1);
								break;

							case CMD_TELEM:
								if( child_cmd_id == CMD_GPS_TELEM )
									gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, 
											SUBSCRIBE_COLUMN, 1,  
											COLOR_COLUMN, "yellow", 
											COLORSET_COLUMN, TRUE,
											-1);
								else 
									gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, 
											SUBSCRIBE_COLUMN, 1,  
											COLOR_COLUMN, "green", 
											COLORSET_COLUMN, TRUE,
											-1);
								break;


								// else fall through to default
							default:
								gtk_tree_store_set (GTK_TREE_STORE (tree_model), &child_iter, 
										SUBSCRIBE_COLUMN, 1,  
										COLOR_COLUMN, "green", 
										COLORSET_COLUMN, TRUE,
										-1);
						}
					}

					valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(tree_model), &child_iter);
				}
			}
		}
		g_free(sys_name);
		valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(tree_model), &sys_iter);
	}

	// update tree view
	gtk_widget_queue_draw(GTK_WIDGET(node_list));

}

gboolean NodeList::timeout_cb(gpointer user_data)
{
	//cout << "LayerManager::updateMap - user_data=" << user_data << endl;

	NodeList *me = (NodeList *)user_data;
	if( !me )
		return false;

	me->refreshNodes();
	
	return TRUE;
}



